/*********************************************************************************
Organization 					: 				Georgia Institute of Technology
  
Institute 						:				Cognitive Computing Group(CCL)
 
Authors							: 				Santiago Ontanon, Kinshuk Mishra
 												Neha Sugandh 
 												
Class							:				Plan
 
Function						: 				Provides a Basic Plan 
												for holding a plan 
****************************************************************************/
package planninglanguage.plans;

import java.io.PrintStream;
import java.io.Serializable;
import java.util.LinkedList;
import java.util.List;

import org.jdom.Element;

import planninglanguage.conditions.AndCondition;
import planninglanguage.conditions.Condition;
import planninglanguage.conditions.OrCondition;
import util.XMLWriter;
import base.WargusGameState;
import cbr.CBR;

abstract public class Plan implements Cloneable, Serializable{
	public static final int PENDING = 0; 
	public static final int READY = 1; 
	public static final int EXECUTING = 2; 
	public static final int FAILED = 3; 
	public static final int SUCCEEDED = 4; 
	
	public static PrintStream m_logger = null;
	
	public static String STATUS_NAME[] = {"PENDING", "READY", "EXECUTING", "FAILED", "SUCCEEDED"};
	
	protected static final String XML_ACTION = "action";
	protected static final String XML_VALUE = "value";
	protected static final String XML_DESTINATION = "destination";
	protected static final String XML_X = "x";
	protected static final String XML_Y = "y";
	
	public Plan(Condition a_precondition,Condition a_aliveCondition,Condition a_successCondition)
	{
		m_status = PENDING;
		m_precondition=a_precondition;
		m_aliveCondition=a_aliveCondition;
		m_successCondition=a_successCondition;
		
		m_ExecutionStartCycle = -1;
		m_ExecutionFinishCycle = -1;
		m_firstExecutionCallCycle = -1;
		
		m_ExecutionStartCycle = -1;
		m_ExecutionFinishCycle = -1;
		m_firstExecutionCallCycle = -1;
		prePlans = new LinkedList<Plan>();
		m_active = true;
	}
	
	abstract public Object clone();
	
	public void resetConditions() {
		if (getPreCondition() != null) getPreCondition().reset();
		if (getAliveCondition() != null) getAliveCondition().reset();
		if (getSuccessCondition() != null) getSuccessCondition().reset();
	}
	
	abstract public void getExecutableActions(LinkedList<BasicOperatorPlan> executableActions);
	
	abstract public void getOpenGoals(LinkedList<SubGoalPlan> openGoals);
	
	abstract public void resetPlanStatus(WargusGameState a_currentGameState);
	
	abstract public void getTentativeActions(LinkedList<BasicOperatorPlan> tentativeActions);
	
	abstract public void getExecutingIds(LinkedList<Integer> executingUnits);
	
	abstract public void updateStatus(WargusGameState a_currentGameState, long a_curCycle, CBR a_cbr);
	
	abstract public void substitutePlan(Plan find, Plan replace);
	
	protected boolean statusComplete() {
		return getStatus() == Plan.SUCCEEDED || getStatus() == Plan.FAILED;
	}
	
	public void setStatus(int a_status)
	{
		m_status = a_status;
	}
	
	public int getStatus()
	{
		return m_status;
	}
	public void setActive(boolean b) {
		m_active = b;
	}
	public void setActive()
	{
		m_active = true;
	}
	public void setInActive()
	{
		m_active = false;
	}
	public boolean getActive()
	{
		return m_active;
	}
	
	public Condition getPreCondition()
	{
		return m_precondition;
	}
	
	public Condition getAliveCondition()
	{
		return m_aliveCondition;
	}
	
	public Condition getSuccessCondition()
	{
		return m_successCondition;
	}
	public void setSuccessCondition(Condition condition) {
		m_successCondition = condition;
	}
	
	protected void setPrecondition(Condition m_precondition) {
		this.m_precondition = m_precondition;
	}

	protected void setAliveCondition(Condition condition) {
		m_aliveCondition = condition;
	}

	protected void setHiddenCondition(Condition condition) {
		m_hiddenCondition = condition;
	}

	protected void setPostCondition(Condition condition) {
		m_postCondition = condition;
	}

	
	public void setContext(WargusGameState a_c) {
		m_context = a_c;
	}
	
	public WargusGameState getContext() {
		return m_context;
	}
	
	public String toString() {
		return toString(0);
	}

	public String toString(int tabs) {
		String str= new String();
		int i;
		
		for(i=0;i<tabs;i++) str = str + "  ";
		str = str + "Plan(status = " + STATUS_NAME[m_status] + ")\n";
		str = str + conditionsToString(tabs);
		return str;
	}

	public String conditionsToString(int tabs) {
		String str= new String();	
	/*	
		int i;
		for(i=0;i<tabs;i++) str = str + "  ";
		if (m_precondition!=null) {
			str = str + "Preconditions:\n" + m_precondition.toString(tabs) + "\n";
		} else {
			str = str + "Preconditions: null\n";
		}
		for(i=0;i<tabs;i++) str = str + "  ";
		if (m_aliveCondition!=null) {
			str = str + "Alive conditions:\n" + m_aliveCondition.toString(tabs) + "\n";
		} else {
			str = str + "Alive conditions: null\n";
		}
		for(i=0;i<tabs;i++) str = str + "  ";
		if (m_successCondition!=null) {
			str = str + "Success conditions:\n" + m_successCondition.toString(tabs);
		} else {
			str = str + "Success conditions: null";
		}
		*/
		return str;
	}

	public void setExecuteStartCycle(long cycle)
	{
		m_ExecutionStartCycle = cycle;
	}
	
	public long getExecuteStartCycle()
	{
		return m_ExecutionStartCycle;
	}	
	
	public void setExecuteFinishCycle(long cycle)
	{
		m_ExecutionFinishCycle = cycle;
	}
	
	public long getExecuteFinishCycle()
	{
		return m_ExecutionFinishCycle;
	}	
	
	public Plan getParent()
	{
		return m_parent;
	}
	
	abstract public void updateParentHierarchy(Plan parent);
	
	public void setParent(Plan p)
	{
		m_parent = p;
	}
	public List<Condition> getFailedConditions(WargusGameState a_gameState)
	{
		List<Condition> l = new LinkedList<Condition>();

		try {
			if(m_precondition !=null)
				getFailedConditions(m_precondition,l,a_gameState);
			if(m_aliveCondition != null)
				getFailedConditions(m_aliveCondition,l,a_gameState);
			if(m_successCondition != null)
				getFailedConditions(m_successCondition,l,a_gameState);
		} catch (Exception e) {
			e.printStackTrace();
		}

		return l;
	}
	
	public void getFailedConditions(Condition c,List<Condition> l,WargusGameState a_gameState)
	{
		try {
			if (!c.test(a_gameState)) {
				if (c instanceof AndCondition) {
					getFailedConditions(((AndCondition)c).getC1(),l,a_gameState);
					getFailedConditions(((AndCondition)c).getC2(),l,a_gameState);
				} else if (c instanceof OrCondition) {
					getFailedConditions(((OrCondition)c).getC1(),l,a_gameState);
					getFailedConditions(((OrCondition)c).getC2(),l,a_gameState);
				} else {
					l.add(c);
					
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public long getFirstExecutionCallCycle()
	{
		return m_firstExecutionCallCycle;
	}
	
	public void setFirstExecutionCallCycle(long cycle)
	{
		m_firstExecutionCallCycle  = cycle;
	}
	public Condition getPostCondition()
	{
		return m_postCondition;
	}
	
	public LinkedList<Plan> getPrePlans()
	{
		return prePlans;
	}
	public void setPrePlans (LinkedList <Plan> a_plans)
	{
		prePlans = a_plans;
	}
	public Condition getHiddenCondition()
	{
		//System.out.println("training "+GameStateImporter.unitTypeToString(m_unitType));
		return m_hiddenCondition;
	}
	
	protected String XMLplanAttributes() {
		return String.format(" status=\"%d\" start-cycle=\"%d\" " + 
				"finish-cycle=\"%d\" first-cycle=\"%d\" ", 
				m_status, m_ExecutionStartCycle, 
				m_ExecutionFinishCycle, m_firstExecutionCallCycle);
	}
	
	protected void writePlanAttrsXML(XMLWriter w) {
		// doesn't save parent b/c that is added when the hierarchy is rebuilt 
		// after all loading is done
		w.tag("context");			
		{
			WargusGameState.writeXML(w, m_context);
		}
		w.tag("/context");

		w.tag("active", Boolean.toString(m_active));

		w.tag("pre-condition");
		{
			Condition.writeXML(w, m_precondition);
		}
		w.tag("/pre-condition");

		w.tag("alive-condition");
		{
			Condition.writeXML(w, m_aliveCondition);
		}
		w.tag("/alive-condition");

		w.tag("hidden-condition");
		{
			Condition.writeXML(w, m_hiddenCondition);
		}
		w.tag("/hidden-condition");

		w.tag("post-condition");
		{
			Condition.writeXML(w, m_postCondition);
		}
		w.tag("/post-condition");

		w.tag("success-condition");
		{
			Condition.writeXML(w, m_successCondition);
		}
		w.tag("/success-condition");
	}

	protected final static void setPlanAttrsFromXML(Plan p, Element e) {
		if (p == null) return;
		
		p.setContext(WargusGameState.loadFromXML(
				e.getChild("context").getChild("description")));
		p.setActive(Boolean.parseBoolean(e.getChildText("active")));
		p.setPrecondition(Condition.loadFromXML(
				e.getChild("pre-condition").getChild("condition")));
		p.setAliveCondition(Condition.loadFromXML(
				e.getChild("alive-condition").getChild("condition")));
		p.setHiddenCondition(Condition.loadFromXML(
				e.getChild("hidden-condition").getChild("condition")));
		p.setPostCondition(Condition.loadFromXML(
				e.getChild("post-condition").getChild("condition")));
		p.setSuccessCondition(Condition.loadFromXML(
				e.getChild("success-condition").getChild("condition")));
	}
	
	abstract public void writeXML(XMLWriter w);

	public static void writeXML(XMLWriter w, Plan p) {
		if (p == null) {
			w.tag("plan", "");
		} else {
			p.writeXML(w);
		}
	}
	
	public static final Plan loadPlanFromXML(Element e) {
		assert e.getName().endsWith("plan"):
			"Plan.loadFromXML: Invalid XML Element " + e.getName();
		
		if (e.getContent().size() == 0) {
			return null;
		} else if (e.getName().equals("plan")) {
			// grab the embedded plan
			//System.err.println("grabbing embedded plan");
			return loadPlanFromXML((Element) e.getChildren().get(0));
		}
		
		String planType = e.getName();
		Plan plan = null;
		if (planType.equals("multi-plan")) {
			plan = SequentialPlan.loadMultiPlanFromXML(e);
		} else if (planType.equals("subgoal-plan")) {
			plan = SubGoalPlan.loadSGPFromXML(e);
		} else if (planType.equals("basicoperator-plan")) {
			plan = BasicOperatorPlan.loadBOPFromXML(e);
		} else {
			throw new Error("Plan.loadFromXML: unknown plan type '" + planType + "'");
		}
		if (plan != null) {
			plan.setStatus(Integer.parseInt(e.getAttributeValue("status")));
			plan.setExecuteStartCycle(Long.parseLong(e.getAttributeValue("start-cycle")));
			plan.setExecuteFinishCycle(Long.parseLong(e.getAttributeValue("finish-cycle")));
			plan.setFirstExecutionCallCycle(Long.parseLong(e.getAttributeValue("first-cycle")));
		}
		
		return plan;
	}

	// not saved to XML b/c MRE can rebuild the plan hierarch using setParents
	protected Plan m_parent = null;  
	protected int m_status; 
	protected long m_ExecutionStartCycle = -1; 
	protected long m_ExecutionFinishCycle = -1;
	protected long m_firstExecutionCallCycle = -1;
	protected Condition m_precondition = null;
	protected Condition m_aliveCondition = null;
	protected Condition m_hiddenCondition = null;	
	protected Condition m_postCondition = null;	
	protected Condition m_successCondition = null;
	protected WargusGameState m_context = null;
	protected boolean m_active;
	LinkedList<Plan> prePlans;
}
